package com.runjian.rundo.iot.agent.iec104.parser.frame;

import java.util.ArrayList;
import java.util.List;

import com.runjian.rundo.iot.agent.iec104.consts.Action;
import com.runjian.rundo.iot.agent.iec104.consts.Cot;
import com.runjian.rundo.iot.agent.iec104.consts.Ti;

import cn.hutool.core.util.StrUtil;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author CaoQingHua
 */

@Getter
@Setter
@Slf4j
public abstract class ASDUFrame implements IASDUFrame {

    /**
     * 类型标识符
     */
    private Ti ti;

    /**
     * 可变结构限定词 sq true - sq=1 地址连续 false - sq=0 地址不连续
     */
    private Boolean sq = Boolean.FALSE;

    /**
     * 可变结构限定词 信息元素个数
     */
    private Short number;

    /**
     * 只用于测试，不会控制处理器&更改系统状态,T=0 (非测试模式), T=1 (测试模式) <0>∶＝未试验 <1>∶＝试验
     */
    private Boolean t = Boolean.FALSE;

    /**
     * 积极/消极的激活确认。 <0>∶＝肯定确认 <1>∶＝否定确认 当使用控制命令时，这个位域是有意义的。该位域通常表示控制命令是否被成功执行。P=0表示肯定确认，P=1表示否定确认。 非控制命令，这个位域总是置0。
     */
    private Boolean pN = Boolean.FALSE;

    /**
     * 传输原因 COT 用于控制通信种的消息路由，该消息路由会把ASDU路由到正确的程序&任务。控制方向的ASDU会相应的服务确认。 COT 是一个6位码, 它被用于描述目标栈点的信息。 0 没有被定义， 1～47用于标准定义。48-63
     * 为特殊目的使用。
     */
    private Cot cot;

    /**
     * 应用服务数据单元公共地址,是一个系统参数，每一个配电终端对应唯一的一个值
     */
    private Short address;

    /**
     * 信息元素集合
     */
    private List<InfoElement> infoElements;

    /**
     * 限定词
     */
    private Short qos;

    /**
     * 组装控制台打印
     *
     * @return 控制台打印字符串
     */
    @Override
    public String console() {
        String head = StrUtil.format(
            "  类型标识符:{}({})\n" + "  可变结构限定词:sq={}\t信息元素个数={}\n" + "  传输原因:{}({})\tT={}\tP/N={}\tASDU公共地址:{}\n"
                + "---------------------------------------------\n",
            ti.getName(), ti.name(), sq ? "地址连续" : "地址不连续", getNumber(), cot.getName(), cot.name(), t ? "未试验" : "试验", pN ? "肯定确认" : "否定确认",
            address);

        StringBuilder body = new StringBuilder();

        for (int i = 1; i <= infoElements.size(); i++) {
            body.append(StrUtil.format("信息元素{}\t{}\n", i, infoElements.get(i - 1).console()));
        }
        if (qos != null) {
            body.append(StrUtil.format("设定命令限定词QOS:{}\n", qos));
        }
        return head + body;
    }

    /**
     * 添加信息元素
     * 
     * @param infoElement 信息元素
     */
    public void addInfoElement(InfoElement infoElement) {
        if (this.infoElements == null) {
            this.infoElements = new ArrayList<>();
        }
        infoElements.add(infoElement);
    }

    /**
     * 解析信息
     * 
     * @param msg 消息内容,各实现的类型根据自身转换成为不同的类型对象
     */
    public void analysisInfo(byte[] msg) {}

    /**
     * 回复信息
     * 
     * @return 回复内容
     */
    public ASDUFrame replyFrame() {
        return null;
    }

    /**
     * 执行动作
     *
     * @return 执行的动作
     */
    public List<Action> action() {
        return null;
    }


    /**
     * 初始化一个对象,生成一个控制对象, 由控制站主动发起
     */
    public void init() {}


    /**
     * 下发写入byteBuf的字节数据
     * @param assembleByteBuf 字对缓存对象
     * @param infoElement 信息对象
     */
    public void writeInfoContent(ByteBuf assembleByteBuf, InfoElement<Object> infoElement){
    }

    public Short getNumber() {
        if(null==number){
            number=(short)this.infoElements.size();
        }
        return number;
    }
}
